/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on Jan 17, 2006
*/
package org.python.pydev.ui.wizards.files;
import java.util.ArrayList;
import java.util.TreeMap;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.text.templates.persistence.TemplatePersistenceData;
import org.eclipse.jface.text.templates.persistence.TemplateStore;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.python.pydev.core.IPythonPathNature;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.templates.PyContextType;
import org.python.pydev.editor.templates.TemplateHelper;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.ui.UIConstants;
import org.python.pydev.ui.dialogs.PythonPackageSelectionDialog;
import org.python.pydev.ui.dialogs.SourceFolder;
/**
* The default creation page may be found at org.eclipse.ui.dialogs.WizardNewFileCreationPage
*/
public abstract class AbstractPythonWizardPage extends WizardPage implements KeyListener {
private IStructuredSelection selection;
private Text textSourceFolder;
private Button btBrowseSourceFolder;
protected Text textPackage;
private Button btBrowsePackage;
protected Text textName;
private String initialTextName = "";
/**
* It is not null only when the source folder was correctly validated
*/
private IContainer validatedSourceFolder;
/**
* It is not null only when the package was correctly validated
*/
private IContainer validatedPackage;
private String packageText;
/**
* This is the project
*/
private IProject validatedProject;
/**
* It is not null only when the name was correctly validated
*/
private String validatedName;
private Text textProject;
private Button btBrowseProject;
public IContainer getValidatedSourceFolder() {
return validatedSourceFolder;
}
public IContainer getValidatedPackage() {
return validatedPackage;
}
public String getPackageText() {
return packageText;
}
public String getValidatedName() {
return validatedName;
}
public IProject getValidatedProject() {
return validatedProject;
}
protected AbstractPythonWizardPage(String pageName, IStructuredSelection selection) {
super(pageName);
setPageComplete(false);
this.selection = selection;
}
private Text lastWithFocus;
protected String lastWithFocusStr;
private List templateList;
private TreeMap<String, TemplatePersistenceData> nameToTemplateData;
private Label labelWarningWillCreate;
private Label labelWarningImageWillCreate;
private void setFocusOn(Text txt, String string) {
if (txt != null) {
//System.out.println("seting focus on:"+string);
txt.setFocus();
lastWithFocus = txt;
lastWithFocusStr = string;
}
}
public void resetFocusOnLast() {
if (lastWithFocus != null) {
//System.out.println("reseting focus on:"+lastWithFocusStr);
lastWithFocus.setFocus();
}
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (visible == true) {
resetFocusOnLast();
}
}
public void createControl(Composite parent) {
// top level group
Composite topLevel = new Composite(parent, SWT.NONE);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
topLevel.setLayout(gridLayout);
topLevel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));
topLevel.setFont(parent.getFont());
boolean previousFilled = true;
//create either source folder
if (shouldCreateSourceFolderSelect()) {
previousFilled = createSourceFolderSelect(topLevel);
} else {
//or the project selection
previousFilled = createProjectSelect(topLevel);
}
//always call the package create (but note that it may either create the package or not).
boolean createPackageSelectFilled = createPackageSelect(topLevel, previousFilled);
if (shouldCreatePackageSelect()) {
previousFilled = createPackageSelectFilled;
}
//always create the name
createNameSelect(topLevel, previousFilled);
// Show description on opening
setErrorMessage(null);
setMessage(null);
setControl(topLevel);
}
/**
* Decide whether a source folder must be selected to complete the dialog.
*
* Subclasses can override.
*
* @return true if a source folder should be selected and false if it shouldn't
*/
protected boolean shouldCreateSourceFolderSelect() {
return true;
}
/**
* Subclasses should override to decide whether a package must be selected to complete the dialog.
* @return true if a package should be selected and false if it shouldn't
*/
protected abstract boolean shouldCreatePackageSelect();
/**
* By default only creates the templates box if a package must be selected.
*/
protected boolean shouldCreateTemplates() {
return shouldCreatePackageSelect();
}
/**
* @param topLevel
*/
protected void createNameSelect(Composite topLevel, boolean setFocus) {
createNameLabel(topLevel);
textName = new Text(topLevel, SWT.BORDER);
textName.addKeyListener(this);
setLayout(null, textName, null);
if (initialTextName != null) {
textName.setText(initialTextName);
}
if (setFocus) {
setFocusOn(textName, "name");
textName.setSelection(textName.getText().length());
}
//just create an empty to complete the line (that needs 3 items in the layout)
Label label = new Label(topLevel, SWT.NONE);
label.setText("");
if (shouldCreateTemplates()) {
final TemplateStore templateStore = TemplateHelper.getTemplateStore();
if (templateStore != null) {
TemplatePersistenceData[] templateData = templateStore.getTemplateData(false);
if (templateData != null && templateData.length > 0) {
//create the template selection
label = new Label(topLevel, SWT.NONE);
label.setText("Template");
templateList = new List(topLevel, SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
fillTemplateOptions(templateData, templateList);
//if in the text, pressing down should go to the templates list
textName.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
if (e.keyCode == SWT.ARROW_DOWN) {
templateList.setFocus();
e.doit = false;
}
}
});
textName.addTraverseListener(new TraverseListener() {
public void keyTraversed(TraverseEvent e) {
if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {
templateList.setFocus();
e.doit = false;
}
}
});
Link link = new Link(topLevel, SWT.NONE);
link.setText("<a>Config...</a>");
link.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null,
"org.python.pydev.prefs.template", null, null);
dialog.open();
//Fill it after having the settings edited.
TemplatePersistenceData[] templateData = templateStore.getTemplateData(false);
if (templateData != null && templateData.length > 0) {
fillTemplateOptions(templateData, templateList);
} else {
fillTemplateOptions(new TemplatePersistenceData[0], templateList);
}
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
setLayout(label, templateList, link);
}
}
}
}
protected Label createNameLabel(Composite topLevel) {
Label label = new Label(topLevel, SWT.NONE);
label.setText("Name");
GridData data = new GridData();
data.grabExcessHorizontalSpace = false;
label.setLayoutData(data);
return label;
}
/**
* @return the data for the selected template or null if not available.
*/
public TemplatePersistenceData getSelectedTemplate() {
if (templateList != null && nameToTemplateData != null) {
String[] sel = templateList.getSelection();
if (sel != null && sel.length > 0) {
return nameToTemplateData.get(sel[0]);
}
}
return null;
}
/**
* Sets the template options in the passed list (swt)
*/
private void fillTemplateOptions(TemplatePersistenceData[] templateData, List list) {
nameToTemplateData = new TreeMap<String, TemplatePersistenceData>();
for (TemplatePersistenceData data : templateData) {
if (PyContextType.PY_MODULES_CONTEXT_TYPE.equals(data.getTemplate().getContextTypeId())) {
String name = data.getTemplate().getName();
nameToTemplateData.put(name, data);
}
}
ArrayList<String> lst = new ArrayList<String>(nameToTemplateData.keySet());
list.setItems(lst.toArray(new String[lst.size()]));
list.setSelection(0);
}
private boolean createProjectSelect(Composite topLevel) {
Label label;
label = new Label(topLevel, SWT.NONE);
label.setText("Project");
textProject = new Text(topLevel, SWT.BORDER);
textProject.addKeyListener(this);
btBrowseProject = new Button(topLevel, SWT.NONE);
setLayout(label, textProject, btBrowseProject);
setFocusOn(textProject, "project");
btBrowseProject.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(),
new WorkbenchLabelProvider());
dialog.setTitle("Project selection");
dialog.setTitle("Select a project.");
dialog.setElements(ResourcesPlugin.getWorkspace().getRoot().getProjects());
dialog.open();
Object[] result = dialog.getResult();
if (result != null && result.length > 0) {
textProject.setText(((IProject) result[0]).getName());
}
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
Object element = selection.getFirstElement();
if (element instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) element;
element = adaptable.getAdapter(IResource.class);
}
if (element instanceof IResource) {
IResource f = (IResource) element;
element = f.getProject();
}
if (element instanceof IProject) {
textProject.setText(((IProject) element).getName());
return true;
}
return false;
}
/**
* @param topLevel
* @return
*/
private boolean createPackageSelect(Composite topLevel, boolean setFocus) {
if (shouldCreatePackageSelect()) {
Label label;
label = new Label(topLevel, SWT.NONE);
label.setText("Package");
textPackage = new Text(topLevel, SWT.BORDER);
textPackage.addKeyListener(this);
btBrowsePackage = new Button(topLevel, SWT.NONE);
setLayout(label, textPackage, btBrowsePackage);
labelWarningImageWillCreate = new Label(topLevel, SWT.NONE);
labelWarningImageWillCreate.setVisible(false);
labelWarningImageWillCreate.setImage(PydevPlugin.getImageCache().get(UIConstants.WARNING));
labelWarningWillCreate = new Label(topLevel, SWT.NONE);
labelWarningWillCreate.setText("Note: package not found (will be created).");
labelWarningWillCreate.setVisible(false);
setLayout(labelWarningImageWillCreate, labelWarningWillCreate, null);
//just create an empty to complete the line (that needs 3 items in the layout)
new Label(topLevel, SWT.NONE);
if (setFocus) {
setFocusOn(textPackage, "package");
}
btBrowsePackage.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
try {
PythonPackageSelectionDialog dialog = new PythonPackageSelectionDialog(getShell(), false);
dialog.open();
Object firstResult = dialog.getFirstResult();
if (firstResult instanceof SourceFolder) { //it is the default package
SourceFolder f = (SourceFolder) firstResult;
textPackage.setText("");
textSourceFolder.setText(f.folder.getFullPath().toString());
}
if (firstResult instanceof org.python.pydev.ui.dialogs.Package) {
org.python.pydev.ui.dialogs.Package f = (org.python.pydev.ui.dialogs.Package) firstResult;
textPackage.setText(f.getPackageName());
textSourceFolder.setText(f.sourceFolder.folder.getFullPath().toString());
}
} catch (Exception e1) {
Log.log(e1);
}
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}
Object element = selection.getFirstElement();
try {
if (element instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) element;
element = adaptable.getAdapter(IFile.class);
if (element == null) {
element = adaptable.getAdapter(IFolder.class);
}
}
if (element instanceof IFile) {
IFile f = (IFile) element;
element = f.getParent();
}
if (element instanceof IFolder) {
IFolder f = (IFolder) element;
String srcPath = getSrcFolderFromFolder(f);
if (srcPath == null) {
return false;
}
String complete = f.getFullPath().toString();
if (complete.startsWith(srcPath)) {
complete = complete.substring(srcPath.length()).replace('/', '.');
if (complete.startsWith(".")) {
complete = complete.substring(1);
}
if (shouldCreatePackageSelect()) {
textPackage.setText(complete);
} else {
initialTextName = complete;
}
}
}
} catch (Exception e) {
Log.log(e);
}
if (shouldCreatePackageSelect()) {
return textPackage.getText().length() > 0;
} else {
return false;
}
}
/**
* @param topLevel
* @return
*/
private boolean createSourceFolderSelect(Composite topLevel) {
Label label;
label = new Label(topLevel, SWT.NONE);
label.setText("Source Folder");
textSourceFolder = new Text(topLevel, SWT.BORDER);
textSourceFolder.addKeyListener(this);
btBrowseSourceFolder = new Button(topLevel, SWT.NONE);
setLayout(label, textSourceFolder, btBrowseSourceFolder);
btBrowseSourceFolder.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
try {
PythonPackageSelectionDialog dialog = new PythonPackageSelectionDialog(getShell(), true);
dialog.open();
Object firstResult = dialog.getFirstResult();
if (firstResult instanceof SourceFolder) {
SourceFolder f = (SourceFolder) firstResult;
textSourceFolder.setText(f.folder.getFullPath().toString());
}
} catch (Exception e1) {
Log.log(e1);
}
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
Object element = selection.getFirstElement();
try {
if (element instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) element;
element = adaptable.getAdapter(IFile.class);
if (element == null) {
element = adaptable.getAdapter(IProject.class);
}
if (element == null) {
element = adaptable.getAdapter(IFolder.class);
}
}
if (element instanceof IFile) {
IFile f = (IFile) element;
element = f.getParent();
}
if (element instanceof IProject) {
IPythonPathNature nature = PythonNature.getPythonPathNature((IProject) element);
if (nature != null) {
String[] srcPaths = PythonNature.getStrAsStrItems(nature.getProjectSourcePath(true));
if (srcPaths.length > 0) {
textSourceFolder.setText(srcPaths[0]);
return true;
}
}
}
if (element instanceof IFolder) {
IFolder f = (IFolder) element;
String srcPath = getSrcFolderFromFolder(f);
if (srcPath == null) {
return true;
}
textSourceFolder.setText(srcPath);
return true;
}
} catch (Exception e) {
Log.log(e);
}
return false;
}
/**
* @param f
* @return
* @throws CoreException
*/
public String getSrcFolderFromFolder(IFolder f) throws CoreException {
IPythonPathNature nature = PythonNature.getPythonPathNature(f.getProject());
if (nature != null) {
String[] srcPaths = PythonNature.getStrAsStrItems(nature.getProjectSourcePath(true));
String relFolder = f.getFullPath().toString() + "/";
for (String src : srcPaths) {
if (relFolder.startsWith(src + "/")) {
return src;
}
}
}
return null;
}
private void setLayout(Label label, Control text, Control bt) {
GridData data;
if (label != null) {
data = new GridData();
data.grabExcessHorizontalSpace = false;
label.setLayoutData(data);
}
if (text != null) {
data = new GridData(GridData.FILL_HORIZONTAL);
data.grabExcessHorizontalSpace = true;
text.setLayoutData(data);
}
if (bt != null) {
data = new GridData();
bt.setLayoutData(data);
if (bt instanceof Button) {
((Button) bt).setText("Browse...");
}
}
}
//listener interface
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
validatePage();
}
protected void validatePage() {
try {
if (textProject != null) {
if (checkError(checkValidProject(textProject.getText()))) {
return;
}
}
if (textSourceFolder != null) {
if (checkError(checkValidSourceFolder(textSourceFolder.getText()))) {
return;
}
}
if (textPackage != null) {
if (checkError(checkValidPackage(textPackage.getText()))) {
return;
}
}
if (textName != null) {
if (checkError(checkValidName(textName.getText()))) {
return;
}
}
if (checkAdditionalErrors()) {
return;
}
setErrorMessage(null);
setMessage(getDescription());
setPageComplete(true);
} catch (Exception e) {
Log.log(e);
setErrorMessage("Error while validating page:" + e.getMessage());
setPageComplete(false);
}
}
/**
* Subclasses may override to do additional error-checking.
* @return
*/
protected boolean checkAdditionalErrors() {
return false;
}
private String checkValidProject(String text) {
validatedProject = null;
if (text == null || text.trim().length() == 0) {
return "The project name must be filled.";
}
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IProject project = root.getProject(text);
if (!project.exists()) {
return "The project selected does not exist in the workspace.";
}
validatedProject = project;
return null;
}
protected boolean checkError(String error) {
if (error != null) {
setErrorMessage(error);
setPageComplete(false);
return true;
}
return false;
}
private String checkValidName(String text) {
validatedName = null;
String error = checkNameText(text);
if (error != null) {
return error;
}
validatedName = text;
return null;
}
protected String checkNameText(String text) {
if (text == null || text.trim().length() == 0) {
return "The name must be filled.";
}
if (shouldCreateSourceFolderSelect()) {
if (validatedSourceFolder == null) {
return "The source folder was not correctly validated.";
}
} else if (validatedProject == null) {
return "The project was not correctly validated.";
}
if (shouldCreatePackageSelect()) {
if (validatedPackage == null && packageText == null) {
return "The package was not correctly validated.";
}
}
if (text.indexOf(' ') != -1) {
return "The name may not contain spaces";
}
if (shouldCreatePackageSelect()) {
//it is a new file not a new package
if (text.indexOf('.') != -1) {
return "The name may not contain dots";
}
}
//there are certainly other invalid chars, but let's leave it like that...
char[] invalid = new char[] { '/', '\\', ',', '*', '(', ')', '{', '}', '[', ']' };
for (char c : invalid) {
if (text.indexOf(c) != -1) {
return "The name must not contain '" + c + "'.";
}
}
if (text.endsWith(".")) {
return "The name may not end with a dot";
}
return null;
}
protected String checkValidPackage(String text) {
validatedPackage = null;
packageText = null;
//there is a chance that the package is the default project, so, the validation below may not be valid.
//if(text == null || text.trim().length() == 0 ){
//}
String initialText = text;
if (text.indexOf('/') != -1) {
labelWarningImageWillCreate.setVisible(false);
labelWarningWillCreate.setVisible(false);
labelWarningWillCreate.getParent().layout();
return "The package name must not contain '/'.";
}
if (text.indexOf('\\') != -1) {
labelWarningImageWillCreate.setVisible(false);
labelWarningWillCreate.setVisible(false);
labelWarningWillCreate.getParent().layout();
return "The package name must not contain '\\'.";
}
if (text.endsWith(".")) {
labelWarningImageWillCreate.setVisible(false);
labelWarningWillCreate.setVisible(false);
labelWarningWillCreate.getParent().layout();
return "The package may not end with a dot";
}
text = text.replace('.', '/');
if (validatedSourceFolder == null) {
labelWarningImageWillCreate.setVisible(false);
labelWarningWillCreate.setVisible(false);
labelWarningWillCreate.getParent().layout();
return "The source folder was not correctly validated.";
}
IPath path = validatedSourceFolder.getFullPath().append(text);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IResource resource = root.findMember(path);
if (resource == null) {
packageText = initialText;
labelWarningImageWillCreate.setVisible(true);
labelWarningWillCreate.setVisible(true);
labelWarningWillCreate.getParent().layout();
return null;
}
labelWarningImageWillCreate.setVisible(false);
labelWarningWillCreate.setVisible(false);
labelWarningWillCreate.getParent().layout();
if (!(resource instanceof IContainer)) {
return "The resource found for the package is not a valid container.";
}
if (!resource.exists()) {
return "The package selected does not exist in the filesystem.";
}
validatedPackage = (IContainer) resource;
return null;
}
private String checkValidSourceFolder(String text) throws CoreException {
validatedSourceFolder = null;
if (text == null || text.trim().length() == 0) {
return "The source folder must be filled.";
}
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IResource resource = root.findMember(new Path(text));
if (resource == null) {
return "The source folder was not found in the workspace.";
}
if (!(resource instanceof IContainer)) {
return "The source folder was found in the workspace but is not a container.";
}
IProject project = resource.getProject();
if (project == null) {
return "Unable to find the project related to the source folder.";
}
IPythonPathNature nature = PythonNature.getPythonPathNature(project);
if (nature == null) {
return "The pydev nature is not configured on the project: " + project.getName();
}
String full = resource.getFullPath().toString();
String[] srcPaths = PythonNature.getStrAsStrItems(nature.getProjectSourcePath(true));
for (String str : srcPaths) {
if (str.equals(full)) {
validatedSourceFolder = (IContainer) resource;
return null;
}
}
return "The selected source folder is not recognized as a valid source folder.";
}
}